/**
 * 
 */
package com.xnkf.common.spring.redis;

import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.SerializationUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * @ClassName: RedisCache
 * @Description: TODO
 * @author xaoyaoyao
 * @date 2017年7月22日 下午7:37:46
 */
public class RedisCache<K, V> implements Cache<K, V> {

	private static final String DEFAULT_SECTION = "redis-cache";

	private static byte[] serialize(Object obj) {
		if (obj == null || !(obj instanceof Serializable)) {
			throw new RuntimeException("参数不能为空且实现Serializable.");
		}
		Serializable sobj = (Serializable) obj;
		return SerializationUtils.serialize(sobj);
	}

	@SuppressWarnings("unchecked")
	private static <T> T deserialize(byte[] data) {
		return (T) SerializationUtils.deserialize(data);
	}

	private JedisPool jedisPool;
	private byte[] section;

	public RedisCache(JedisPool jedisPool, String section) {
		this.jedisPool = jedisPool;
		this.section = section.getBytes(Charset.forName("UTF-8"));
	}

	public RedisCache(JedisPool jedisPool) {
		this(jedisPool, DEFAULT_SECTION);
	}

	@Override
	public void clear() throws CacheException {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.del(section);
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

	}

	@Override
	public V get(K name) throws CacheException {
		if (name == null) {
			return null;
		}
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			byte[] key = serialize(name);
			byte[] value = jedis.hget(section, key);
			if (value == null) {
				return null;
			}
			return deserialize(value);
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	@Override
	public Set<K> keys() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			Set<byte[]> keys = jedis.hkeys(section);
			if (keys == null || keys.isEmpty()) {
				return Collections.emptySet();
			}
			Set<K> ks = new HashSet<K>(keys.size());
			for (byte[] key : keys) {
				K k = deserialize(key);
				ks.add(k);
			}
			return ks;
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	@Override
	public V put(K key, V value) throws CacheException {
		if (key == null || value == null) {
			return null;
		}
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			byte[] k = serialize(key);
			byte[] v = serialize(value);
			jedis.hset(section, k, v);
			return value;
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	@Override
	public V remove(K key) throws CacheException {
		if (key == null) {
			return null;
		}
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			byte[] k = serialize(key);
			byte[] value = jedis.hget(section, k);
			if (value == null) {
				return null;
			}
			jedis.hdel(section, k);
			return deserialize(value);
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	@Override
	public int size() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hlen(section).intValue();
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	@Override
	public Collection<V> values() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			List<byte[]> values = jedis.hvals(section);
			if (values == null || values.isEmpty()) {
				return Collections.emptyList();
			}
			List<V> vs = new ArrayList<V>(values.size());
			for (byte[] value : values) {
				V v = deserialize(value);
				vs.add(v);
			}
			return vs;
		} catch (Exception e) {
			throw new CacheException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}
}
